package ru.ya.kolemik.servcmd.servlets;

import static ru.ya.kolemik.servcmd.View.type.*;

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.log4j.Logger;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.ClassPathResource;

import ru.ya.kolemik.servcmd.MainApp;
import ru.ya.kolemik.servcmd.PropertyHelper;
import ru.ya.kolemik.servcmd.Serv;
import ru.ya.kolemik.servcmd.Utils;
import ru.ya.kolemik.servcmd.View;
import ru.ya.kolemik.servcmd.servs.ServContext;

public class Controller extends HttpServlet {
    /**
     * 
     */
    private static final long serialVersionUID = 6569223144147713123L;
    
    private static final String CONFIG_NAME = "config.name";
    private static final String MAIN_APP_BEAN_NAME = "main.app.bean";
    
    // private String[] patterns = new String[] { "/JSP/", "/IMG/"};
    
    private final Logger log = Logger.getLogger(getClass());
    
    private BeanFactory beanFactory;
    
    private MainApp mainApp;
    
    @Override
    public void init(ServletConfig config) throws ServletException {
        log.info("Initialyzing Controller");
        String configName = PropertyHelper.getProperty(CONFIG_NAME);
        String beanName = PropertyHelper.getProperty(MAIN_APP_BEAN_NAME);
        this.beanFactory = new XmlBeanFactory(new ClassPathResource(configName));
        this.mainApp = (MainApp) beanFactory.getBean(beanName);
        super.init(config);
    }
    
    @Override
    protected void service(HttpServletRequest httpRequest,
            HttpServletResponse response) throws ServletException, IOException {
        log.debug("Controller");
        log.debug("Req URI = " + (String) httpRequest.getRequestURI());
        log
                .debug("Req_I_URI = "
                        + httpRequest
                                .getAttribute("javax.servlet.include.request_uri"));
        log
                .debug("Req_F_URI = "
                        + httpRequest
                                .getAttribute("javax.servlet.forward.request_uri"));
        
        String uri = (String) httpRequest
                .getAttribute("javax.servlet.include.request_uri");
        if (uri == null) {
            uri = httpRequest.getRequestURI().substring(
                    httpRequest.getContextPath().length());
        } else {
            uri = uri.substring(httpRequest.getContextPath().length());
        }
        /*
         * log.debug("Controller - URI: " + uri); if (uri != null) { for (String
         * prefix : patterns) { if (uri.startsWith(prefix)) {
         * chain.doFilter(request, response); return; } } } else { throw new
         * ServletException("Unexpected request!" +
         * httpRequest.getRequestURI()); }
         */

        // Получаем контекст выполнения цепочки сёрвов
        ServContext context = (ServContext) httpRequest
                .getAttribute(ServContext.REQUEST_ATTRIBUTE_NAME);
        // Получаем команду
        String command = Utils.getCommand(httpRequest);
        log.debug("command = " + command);
        
        // Проверяем, ести ли контекст
        if (context == null) {
            
            // Мы только начали цепочку обработки сёрвов - создаём новый
            // контекст и пихаем в реквест
            context = new ServContext(command);
            httpRequest.setAttribute(ServContext.REQUEST_ATTRIBUTE_NAME,
                    context);
            
            // Если ещё и команда пустая, то надо решить что же пользователь
            // хочет увидеть "по-умолчанию"
            if (command == null) {
                command = mainApp.getDefaultCommand();
            }
            
            // Тут надо вставить код определения нужного типа вью, хотя можно и
            // обойтись - пусть каждый корневой сёрв вставляет этот тип сам.
            // Типа для запроса вида /ajax/list сёрв AJAX выставит тип view в
            // xml, а для запроса /pda/search поработает сёрв pda с html view.
            String[] view_command = Utils.processCommand(command);
            View.type view_type = null;
            try {
                view_type = View.type.valueOf(view_command[0]);
                if (view_type != null) {
                    command = view_command[1];
                }
            } catch (IllegalArgumentException e) {
                view_type = html;
            }
            log.info("Installing new Context");
            
            context.setViewType(view_type);
        }
        
        // LOGIN
        // Сёрв должен вернуть команду (ту, которая в контексте) или же самого
        // себя (если произошла ошибка)
        Serv serv = (Serv) beanFactory.getBean("auth");
        command = serv.doServe(httpRequest, response, command);
        
        // Если не получилось залогиниться, то command будет равно null.
        // Если залогинились, то command будет тот, что был передан в
        // контроллер.
        while (command != null) {
            String[] parsedCommand = Utils.processCommand(command);
            log.debug("Command = [" + parsedCommand[0] + ", "
                    + parsedCommand[1] + "]");
            if (beanFactory.containsBean(parsedCommand[0])) {
                serv = (Serv) beanFactory.getBean(parsedCommand[0]);
                log.debug("BeanClass = " + serv.getClass());
                
                // Authorization
                // Если сёрв поддерживает авторизацию - то авторизовывать через
                // него
                // if (serv instanceof AuthorizeServ) {
                // if (!((AuthorizeServ)serv).authorize(context.getUser(),
                // parsedCommand[1])) {
                // throw new ServletException();
                // }
                // }
                command = serv.doServe(httpRequest, response, parsedCommand[1]);
            } else {
                String message = "No Servs found with name = "
                        + parsedCommand[0];
                log.error(message);
                throw new ServletException(message);
            }
        }
        navigateToView(httpRequest, response, context, serv);
    }
    
    private void navigateToView(HttpServletRequest request,
            HttpServletResponse response, ServContext context, Serv serv)
            throws ServletException {
        log.info("Navigate to view with type = " + context.getViewType());
        Method view = serv.getViewMethod(context.getViewType());
        if (view == null) {
            String message = "For Serv = \"" + serv + "\" view \""
                    + context.getViewType() + "\" is not defined.";
            log.error(message);
            view = serv.getViewMethod(html);
            if (view == null) {
                throw new ServletException(message);
            } else {
                // context.setViewType(html);
            }
        }
        log.debug("View Method = " + view);
        try {
            view.invoke(serv, request, response);
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }
}
